package se.webbzon.boltzmann.editor.h3ds;

import java.awt.Color;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashMap;
import java.util.Map;

import javax.vecmath.Vector3f;

import se.webbzon.boltzmann.object.geometry.Geometry;
import se.webbzon.boltzmann.object.geometry.GeometryAppearance;
import se.webbzon.boltzmann.object.geometry.GeometryEnum;
import se.webbzon.boltzmann.object.geometry.GeometryMask;
import se.webbzon.boltzmann.object.light.DirectionalLight;
import se.webbzon.boltzmann.object.unknown.Unknown;
import se.webbzon.oschi01.worldarea.WorldArea;
import se.webbzon.oschi01.worldobject.WorldStaticObject;
import se.webbzon.oschi01.worldobject.appearance.Appearance3DException;

public class H3DSReader {
	
	final static double degToRad = Math.PI / 180.0;
	
	// A set of strings associated to the available geometries
	Map<String,GeometryEnum> geometries;
	
	// The H3DS texture map
	H3DSTextureMap textureMap;
	
	/*============================================================
	Constructors
	============================================================*/
	
	public H3DSReader() {
		geometries = new HashMap<String,GeometryEnum>();
		geometries.put("o_block",GeometryEnum.CUBE);
		geometries.put("o_water_block",GeometryEnum.CUBE);
		geometries.put("o_cylinder",GeometryEnum.CYLINDER);
		geometries.put("o_water_cylinder",GeometryEnum.CYLINDER);
		geometries.put("o_cone",GeometryEnum.CONE);
		geometries.put("o_ellipsoid",GeometryEnum.ELLIPSOID);
		geometries.put("o_wall",GeometryEnum.XYPLANE);
		geometries.put("o_floor",GeometryEnum.XZPLANE);
		
		textureMap = new H3DSTextureMap();
	}
	
	/*============================================================
	Public Methods
	============================================================*/
	
	/** Loads the content of a 3ds-file into a WorldArea instance
	 * by using an input stream. **/
	public WorldArea load(InputStream inputStream) throws IOException {
		BufferedReader reader = 
				new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));
		return loadWorldArea(reader);
	}
	
	/** Loads the content of a 3ds-file into a WorldArea instance
	 * by using an url. **/
	public WorldArea load(URL url) throws IOException {
		URLConnection connection = url.openConnection();
		InputStream inputStream = connection.getInputStream();
		WorldArea world = load(inputStream);
		inputStream.close();
		return world;
	}
	
	/*============================================================
	Private Methods
	============================================================*/
	
	private WorldArea loadWorldArea(BufferedReader reader) throws IOException {
		WorldArea world = new WorldArea(WorldArea.RENDERING_ORDER);
		
		// Read light
		Color lightingColor = new Color(Integer.parseInt(reader.readLine()));
		world.createInstance(new DirectionalLight(new Vector3f(1,-1,-1),lightingColor));
		world.createInstance(new DirectionalLight(new Vector3f(-1,-1,-1),lightingColor));
		
		// Skip 11 lines
		for (int i = 0;i < 11; i++)
			reader.readLine();
		
		while (reader.ready()) {
			String obj = reader.readLine();
			boolean solid = false;
			GeometryEnum g = geometries.get(obj);
			if (g != null && !obj.startsWith("o_water"))
				solid = !reader.readLine().equals("0");
			double x = Double.parseDouble(reader.readLine()) - 512;
			double y = Double.parseDouble(reader.readLine()) - 320;
			double z = Double.parseDouble(reader.readLine());
			// Only available for 3ds
			double xLayer = Double.parseDouble(reader.readLine());
			double yLayer = Double.parseDouble(reader.readLine());
			double zLayer = Double.parseDouble(reader.readLine());
			double w = Double.parseDouble(reader.readLine());
			double h = Double.parseDouble(reader.readLine());
			double d = Double.parseDouble(reader.readLine());
			double xrot = Double.parseDouble(reader.readLine());
			double yrot = Double.parseDouble(reader.readLine());
			double zrot = Double.parseDouble(reader.readLine());
			boolean visible = !reader.readLine().equals("0");
			Color color = new Color(Integer.parseInt(reader.readLine()));
			color = new Color(color.getBlue(),color.getGreen(),color.getRed());
			boolean closed = !reader.readLine().equals("0");
			int steps = Integer.parseInt(reader.readLine());
			int hrep = Integer.parseInt(reader.readLine());
			int vrep = Integer.parseInt(reader.readLine());
			File textureFile = new File(reader.readLine());
			String texture = textureFile.getName().replaceFirst("[.][^.]+$", "");
			if (!texture.isEmpty())
				texture = textureMap.get(texture);
			String model = "";
			if (obj.equals("o_model")) {
				model = reader.readLine();
			}
			
			// Create instance
			WorldStaticObject instance;
			if (g != null) {
				if (g == GeometryEnum.CUBE && h == 0) {
					h = 1;
					g = GeometryEnum.XZPLANE;
					obj = "o_xzplane";
				}
				GeometryMask mask = new GeometryMask(g,!closed,steps);
				boolean backfaced = (w * h * d < 0);
				GeometryAppearance appearance = new GeometryAppearance(g, !closed, steps, backfaced, 0, texture, 0, 0, xLayer, -yLayer, zLayer, color, obj.startsWith("o_water") ? 0.5f : 0, hrep, vrep, 0, 0, 0);
				Geometry geometry = null; 
				try {
					geometry = new Geometry(appearance,visible,mask,solid);
				} catch (Appearance3DException e) {
					throw new IOException(e);
				}
				instance = geometry;
			} else
				instance = new Unknown();
	
			instance.setPosition(x,-y,z);
			if (obj.equals("o_cone") || obj.equals("o_cylinder"))
				instance.setSize(Math.abs(w),Math.abs(d),Math.abs(h));
			else {
				instance.setSize(Math.abs(w),Math.abs(h),Math.abs(d));
				if (obj.equals("o_wall") || obj.equals("o_floor")) {
					// Do nothing
				} else {
					if (xrot == 0 && yrot == 0)
						instance.setRotation(0, 0, 1, degToRad * zrot);
					else if (xrot == 0 && zrot == 0)
						instance.setRotation(0, 1, 0, -degToRad * yrot);
					else
						instance.setRotation(1, 0, 0, degToRad * xrot);
				}
			}
			instance.setSolid(solid);
			instance.setVisible(visible);
			
			world.createInstance(instance);
		}
		
		return world;
	}

}
